// Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.DirectX.Direct3D11;
using Microsoft.WindowsAPICodePack.DirectX.DXGI;

namespace Microsoft.WindowsAPICodePack.Samples.Direct3D11
{
    public partial class Form1 : Form
    {
        private struct D3DVector3F
        {
            public float x;
            public float y;
            public float z;
        }

        private struct SimpleVertex
        {
            public D3DVector3F Pos;
        };

        public Form1()
        {
            InitializeComponent();
                        
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);            
            UpdateStyles();

        }

        D3DDevice device;
        DeviceContext deviceContext;
        RenderTargetView renderTargetView;
        PixelShader pixelShader;
        VertexShader vertexShader;
        D3DBuffer vertexBuffer;
        SwapChain swapChain;

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);

            // device creation
            device = D3DDevice.CreateDeviceAndSwapChain(Handle, out swapChain);
            deviceContext = device.GetImmediateContext();

            
            Texture2D texture2D = swapChain.GetBuffer<Texture2D>(0);
            renderTargetView = device.CreateRenderTargetView(texture2D, null);
            deviceContext.OM.SetRenderTargets(new RenderTargetView[] { renderTargetView }, null);
            texture2D.Dispose();

            // viewport
            SwapChainDescription desc = swapChain.GetDescription();
            Viewport viewport = new Viewport();
            viewport.Width = desc.BufferDesc.Width;
            viewport.Height = desc.BufferDesc.Height;
            viewport.MinDepth = 0.0f;
            viewport.MaxDepth = 1.0f;
            viewport.TopLeftX = 0;
            viewport.TopLeftY = 0;

            deviceContext.RS.SetViewports(new Viewport[] { viewport });


            // raster state


            // vertex shader & layout            

            // Open precompiled vertex shader
            // This file was compiled using: fxc Render.hlsl /T vs_4_0 /EVertShader /FoRender.vs
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.WindowsAPICodePack.Samples.Direct3D11.Render.vs"))
            {
                vertexShader = device.CreateVertexShader(stream);
            }

            deviceContext.VS.SetShader(vertexShader, null);

            // input layout is for the vert shader
            InputElementDescription inputElementDescription = new InputElementDescription();
            inputElementDescription.SemanticName = "POSITION";
            inputElementDescription.SemanticIndex = 0;
            inputElementDescription.Format = Format.R32G32B32_FLOAT;
            inputElementDescription.InputSlot = 0;
            inputElementDescription.AlignedByteOffset = 0;
            inputElementDescription.InputSlotClass = InputClassification.PerVertexData;
            inputElementDescription.InstanceDataStepRate = 0;

            InputLayout inputLayout;
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.WindowsAPICodePack.Samples.Direct3D11.Render.vs"))
            {
                inputLayout = device.CreateInputLayout(new InputElementDescription[] { inputElementDescription }, stream);
            }
            deviceContext.IA.SetInputLayout(inputLayout);

            // Open precompiled vertex shader
            // This file was compiled using: fxc Render.hlsl /T ps_4_0 /EPixShader /FoRender.ps
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.WindowsAPICodePack.Samples.Direct3D11.Render.ps"))
            {
                pixelShader = device.CreatePixelShader(stream);
            }
            deviceContext.PS.SetShader(pixelShader, null);

            
            // create some geometry to draw (1 triangle)

            SimpleVertex[] verts2 = new SimpleVertex[3];

            verts2[0].Pos.x = 0.0f;
            verts2[0].Pos.y = 0.5f;
            verts2[0].Pos.z = 0.5f;

            verts2[1].Pos.x = 0.5f;
            verts2[1].Pos.y = -0.5f;
            verts2[1].Pos.z = 0.5f;

            verts2[2].Pos.x = -0.5f;
            verts2[2].Pos.y = -0.5f;
            verts2[2].Pos.z = 0.5f; 


            // put the vertices into a vertex buffer

            BufferDescription bufferDescription = new BufferDescription();
            bufferDescription.Usage = Usage.Default;
            bufferDescription.ByteWidth = (uint) (Marshal.SizeOf(typeof(SimpleVertex)) * verts2.Length);
            bufferDescription.BindFlags = BindFlag.VertexBuffer;

            SubresourceData subresourceData = new SubresourceData();

            unsafe
            {
                fixed (SimpleVertex* pVerts = verts2)
                {
                    subresourceData.SysMem = new IntPtr(pVerts);
                    vertexBuffer = device.CreateBuffer(bufferDescription, subresourceData);
                }
            }

            deviceContext.IA.SetVertexBuffers(0, new D3DBuffer[] { vertexBuffer }, new uint[] { 12 }, new uint[] { 0 });
            deviceContext.IA.SetPrimitiveTopology(PrimitiveTopology.TriangleList);            
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // Do not paint to prevent flickering
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            // Not required unless we need other controls!
            // base.OnPaint(e);  

            deviceContext.ClearRenderTargetView(renderTargetView, new ColorRGBA( new float[] {0.2f, 0.125f, 0.3f, 1.0f} ));            

            deviceContext.Draw(3, 0);

            swapChain.Present(0, 0);
        }


        protected override void OnClosed(EventArgs e)
        {
            // dispose all the DirectX bits

            deviceContext.ClearState();
            deviceContext.Flush();


            if (vertexBuffer != null)
            {
                vertexBuffer.Dispose();
            }

            if (vertexShader != null)
            {
                vertexShader.Dispose();
            }

            if (pixelShader != null)
            {
                pixelShader.Dispose();
            }

            if (renderTargetView != null)
            {
                renderTargetView.Dispose();
            }

            if (device != null)
            {
                device.Dispose();
            }
  
            base.OnClosed(e);
        }

        private void Form1_ResizeEnd(object sender, EventArgs e)
        {
            Invalidate(false);
        }

    }
}
